﻿using System.ComponentModel;
using System.Diagnostics;
using System.Linq;

namespace System
{
    /// <summary>Класс методов расширений для обработчиков событий</summary>
    public static class EventHandlerExtension
    {
        /// <summary>Потоко-безопасная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <param name="e">Аргумент события</param>
        [DebuggerStepThrough]
        public static void Start(this EventHandler Handler, object Sender, EventArgs e)
        {
            var lv_Handler = Handler;
            if(lv_Handler == null) return;
            var invokations = lv_Handler.GetInvocationList();
            for(var i = 0; i < invokations.Length; i++)
            {
                var lv_T = invokations[i];
                if(lv_T.Target is ISynchronizeInvoke && ((ISynchronizeInvoke)lv_T.Target).InvokeRequired)
                    ((ISynchronizeInvoke)lv_T.Target).Invoke(lv_T, new[] { Sender, e });
                else
                    lv_T.DynamicInvoke(Sender, e);
            }
        }

        /// <summary>Потоко-безопасная асинхронная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <param name="e">Аргумент события</param>
        /// <param name="CallBack">Метод завершения генерации события</param>
        /// <param name="State">Объект-состояние, Передаваемый в метод завершения генерации события</param>
        [DebuggerStepThrough]
        public static void StartAsync(this EventHandler Handler, object Sender, EventArgs e,
            AsyncCallback CallBack = null, object @State = null)
        {
            var lv_Handler = Handler;
            if(lv_Handler != null)
                lv_Handler.BeginInvoke(Sender, e, CallBack, State);
        }

        /// <summary>Быстрая генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        [DebuggerStepThrough]
        public static void FastStart(this EventHandler Handler, object Sender)
        {
            var lv_Handler = Handler;
            if(lv_Handler != null)
                Handler.Invoke(Sender, EventArgs.Empty);
        }

        /// <summary>Быстрая генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <param name="e">Аргументы события</param>
        [DebuggerStepThrough]
        public static void FastStart(this EventHandler Handler, object Sender, EventArgs e)
        {
            var lv_Handler = Handler;
            if(lv_Handler != null)
                Handler.Invoke(Sender, e);
        }

        /// <summary>Быстрая генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TEventArgs">Тип аргумента события</typeparam>
        /// <param name="e">Аргументы события</param>
        [DebuggerStepThrough]
        public static void FastStart<TEventArgs>(this EventHandler<TEventArgs> Handler, object Sender, TEventArgs e)
            where TEventArgs : EventArgs
        {
            var lv_Handler = Handler;
            if(lv_Handler != null)
                Handler.Invoke(Sender, e);
        }

        /// <summary>Потоко-безопасная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TEventArgs">Тип аргумента события</typeparam>
        /// <param name="e">Аргументы события</param>
        [DebuggerStepThrough]
        public static void Start<TEventArgs>(this EventHandler<TEventArgs> Handler, object Sender, TEventArgs e)
            where TEventArgs : EventArgs
        {
            var lv_Handler = Handler;
            if(lv_Handler == null) return;
            var invokations = lv_Handler.GetInvocationList();
            for(var i = 0; i < invokations.Length; i++)
            {
                var I = invokations[i];
                if(I.Target is ISynchronizeInvoke && ((ISynchronizeInvoke)I.Target).InvokeRequired)
                    ((ISynchronizeInvoke)I.Target).Invoke(I, new[] { Sender, e });
                else
                    I.DynamicInvoke(Sender, e);
            }
        }

        /// <summary>Потоко-безопасная асинхроная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TEventArgs">Тип аргумента события</typeparam>
        /// <param name="e">Аргументы события</param>
        /// <param name="CallBack">Метод завершения генерации события</param>
        /// <param name="State">Объект-состояние, Передаваемый в метод завершения генерации события</param>
        [DebuggerStepThrough]
        public static void StartAsync<TEventArgs>(this EventHandler<TEventArgs> Handler,
            object Sender, TEventArgs e, AsyncCallback CallBack = null, object @State = null)
            where TEventArgs : EventArgs
        {
            var lv_Handler = Handler;
            if(lv_Handler != null)
                lv_Handler.BeginInvoke(Sender, e, CallBack, State);
        }

        /// <summary>Потоко-безопасная генерация события</summary>
        /// <param name="Handler">Обработчик события</param>
        /// <param name="Sender">Источник события</param>
        /// <typeparam name="TArgs">Тип аргумента события</typeparam>
        /// <param name="Args">Аргументы события</param>
        /// <typeparam name="TResult">Тип результата обработки события</typeparam>
        /// <typeparam name="TSender">Тип источника события</typeparam>
        /// <returns>Массив результатов обработки события</returns>
        [DebuggerStepThrough]
        public static TResult[] Start<TResult, TSender, TArgs>(this EventHandler<TResult, TSender, TArgs> Handler,
            TSender Sender, TArgs Args)
        {
            var lv_Handler = Handler;
            if(lv_Handler == null) return new TResult[0];
            //var invokations = lv_Handler.GetInvocationList();
            //var Result = new TResult[invokations.Length];

            //for(var i = 0; i < invokations.Length; i++)
            //{
            //    var I = invokations[i];
            //    Result[i] = (TResult)(I.Target is ISynchronizeInvoke && ((ISynchronizeInvoke)I.Target).InvokeRequired
            //                                ? ((ISynchronizeInvoke)I.Target).Invoke(I, new object[] { Sender, Args })
            //                                : I.DynamicInvoke(Sender, Args));
            //}
            //return Result;

            return lv_Handler.GetInvocationList().Select(I =>
                        (TResult)(I.Target is ISynchronizeInvoke && ((ISynchronizeInvoke)I.Target).InvokeRequired
                                                ? ((ISynchronizeInvoke)I.Target).Invoke(I, new object[] { Sender, Args })
                                                : I.DynamicInvoke(Sender, Args))).ToArray();
        }
    }
}
